use common_uu::IResult;
// use common::MyError;
use image::{open, GenericImageView, Rgba};

#[test]
fn test() {
    for n in 1..=20 {
        let s = std::time::SystemTime::now();
        let n = {
            if n < 10 {
                format!("0{}", n)
            } else {
                format!("{}", n)
            }
        };
        let big = format!("E:\\guoyu\\Downloads\\网易易盾\\{}-01.jpg", &n);
        let v = run(
            big.clone(),
            format!("E:\\guoyu\\Downloads\\网易易盾\\{}-02.png", n),
        );
        // let s2 = std::time::SystemTime::now();
        let s3 = s.elapsed().unwrap().as_millis();
        println!("image_handle time:{}, {:?}:{:?}", s3, big, v);
    }
}

/// 测试y座标
#[test]
fn test_y() {
    for n in 1..9 {
        let v = find_y_formblock(format!("E:\\guoyu\\Downloads\\网易易盾\\0{}-02.png", n));
        println!("image_handle2 move y:{:?}", v);
    }
}

//可调节参数（当前参数即有很高的识别率故不作为参数接收，仅在识别出错时调整）
//通常情况下调节优先顺序为从上至下，即首先排查扫描幅度，没效果再依次往下，通常情况
//为控制变量当调节某一参数时应保证其它参数恢复为初始值

/**
扫描幅度SCAN_RANGE（向上调整； 调整单位幅度不大于1）
！！该参数会严重影响识别效率           时间增量 △t = 186 * △SCAN_RANGE   (ms)
 */
const SCAN_RANGE: usize = 2;

/**
非同色线不同色权重NOT_SIMILAR_LINE_NOT_SIMILAR_WEIGHT（向上或向下调整； 调整幅度不大于5）
 */
const NOT_SIMILAR_LINE_NOT_SIMILAR_WEIGHT: u32 = 40;

/**
任意线同色权重LINE_SIMILAR_WEIGHT（向上或向下调整； 调整单位幅度不大于5）
 */
const LINE_SIMILAR_WEIGHT: u32 = 20;

/**
同色线不同色权重SIMILAR_LINE_NOT_SIMILAR_WEIGHT（向上或向下调整； 调整单位幅度不大于5）
 */
const SIMILAR_LINE_NOT_SIMILAR_WEIGHT: u32 = 50;

/**
任意线不同色权重LINE_NOT_SIMILAR_WEIGHT（向上或向下调整； 调整单位幅度不大于5）
 */
const LINE_NOT_SIMILAR_WEIGHT: u32 = 60;

/**
任意线相似权重LINE_WEIGHT（向上或向下调整； 调整单位幅度不大于5）
 */
const LINE_WEIGHT: u32 = 40;

/**
同色线相似权重SIMILAR_LINE_WEIGHT（向上或向下调整； 调整单位幅度不大于2）
 */
const SIMILAR_LINE_WEIGHT: u32 = 10;

/**
同色线同色权重SIMILAR_LINE_SIMILAR_WEIGHT（向上或向下调整； 调整单位幅度不大于1）
 */
const SIMILAR_LINE_SIMILAR_WEIGHT: u32 = 0;

/**
非同色线同色权重NOT_SIMILAR_LINE_SIMILAR_WEIGHT（向上或向下调整； 调整单位幅度不大于1）
 */
const NOT_SIMILAR_LINE_SIMILAR_WEIGHT: u32 = 0;

/**
非同色线相似权重NOT_SIMILAR_LINE_WEIGHT（向上或向下调整； 调整单位幅度不大于1）
 */
const NOT_SIMILAR_LINE_WEIGHT: u32 = 3;

/**
不相似标准NOT_SIMILAR_RANK（向上或向下调整； 调整单位幅度不大于5）
 */
const NOT_SIMILAR_RANK: f64 = 80_f64;

/**
相似标准SIMILAR_RANK（向上或向下调整； 调整单位幅度不大于0.5）
 */
const SIMILAR_RANK: f64 = 10_f64;
//根据滑块图片确定Y坐标
fn find_y_formblock(block_path: String) -> IResult<u32> {
    let blockpic = open(block_path)?;
    let mut up: u32 = 0;
    let mut down: u32 = 0;
    let mut upp: bool = false;
    let mut downp: bool = false;
    for y in 0..blockpic.height() {
        //遍历滑块的纵坐标
        if !upp {
            for x in 0..blockpic.width() {
                //遍历滑块的横坐标
                let pixel: Rgba<u8> = blockpic.get_pixel(x, y);
                if pixel[0] != 0 || pixel[1] != 0 || pixel[2] != 0 {
                    up = y;
                    upp = true;
                    break;
                }
            }
        }
        if !downp {
            for x in 0..blockpic.width() {
                //遍历滑块的横坐标
                let pixel: Rgba<u8> = blockpic.get_pixel(x, blockpic.height() - y - 1);
                if pixel[0] != 0 || pixel[1] != 0 || pixel[2] != 0 {
                    down = blockpic.height() - y - 1;
                    downp = true;
                    break;
                }
            }
        }
        if upp && downp {
            break;
        }
    }
    let r = (down - up + 1) * 18 / 29 + up - 1;
    Ok(r)
}
pub fn run(picpath: String, blockpath: String) -> IResult<f64> {
    /*
     *根据中心点的坐标遍历位图,如果能找出轮廓则直接返回此中心坐标
     *程序中的数字常量建立在滑块大小固定的前提下，故写死
     */
    //根据滑块图片计算得出Y轴坐标
    let location_y = find_y_formblock(blockpath)? as i32;
    let pic = open(picpath).unwrap();
    let width = pic.width();

    //此数组代表每个坐标可能是轮廓的权值的大小，权值越大代表此坐标是轮廓的可能性就越大
    let mut weights = [0; 380 + 560 * SCAN_RANGE];
    let mut index_of_weigth = 0;

    //这是一个二维数组，每个一维数组代表一条线共4条
    //每一条线长为14个像素对应滑块每一条小边的长度，
    //因为轮廓多为毛边故取4条作解析
    let mut lines = vec![vec![Rgba([0_u8; 4]); 14]; 4];

    #[allow(unused_assignments)]
        let mut index_y = 0;
    #[allow(unused_assignments)]
        let mut index_x = 0;

    //滑块轮廓，共8条小边，不考虑圆弧
    /*
    _____    _____
    |                 |<————周围的8条小边
            .<———像素点
    |                 |
    ˉˉˉˉˉˉ     ˉˉˉˉˉˉ
    */

    //外层的两层循环为遍历要扫描的像素点，SCAN_RANGE越大要处理的像素点越多
    for y in location_y - SCAN_RANGE as i32..=location_y + SCAN_RANGE as i32 {
        for i in 20..(pic.width() as i32) - 20 {
            //根据每一个像素点获取到其四周的8条小边
            index_y = 0;
            for m in (y - 22)..=(y - 19) {
                index_x = 0;
                for n in (i - 20)..=(i - 7) {
                    if m < 0 {
                        lines[index_y][index_x] = Rgba::<u8>([255, 255, 255, 0]);
                    } else {
                        lines[index_y][index_x] = pic.get_pixel(n as u32, m as u32);
                    }
                    index_x += 1;
                }
                index_y += 1;
            }

            // 将左上横边的像素赋值给lines,并根据lines获取权重
            // 使用get方法, 修复: thread 'tokio-runtime-worker' panicked at 'index out of bounds: the len is 1400 but the index is 1400'
            // index 方法改为 get 并且使用问号向上抛异常
            {
                let v = weights.get_mut(index_of_weigth).ok_or_else(|| format!("index_of_weigth: [{}] is not found", index_of_weigth))?;
                *v += get_weight_bylines(&lines);
            }


            //以下为同样的处理对应其它7条小边，并累加每一条边的权重值作为该像素点对应的
            //总权重
            index_y = 0;
            for m in (y - 22)..=(y - 19) {
                index_x = 0;
                for n in (i + 7)..=(i + 20) {
                    if m < 0 {
                        lines[index_y][index_x] = Rgba::<u8>([255, 255, 255, 0]);
                    } else {
                        lines[index_y][index_x] = pic.get_pixel(n as u32, m as u32);
                    }
                    index_x += 1;
                }
                index_y += 1;
            }
            weights[index_of_weigth] += get_weight_bylines(&lines);
            index_x = 0;
            for m in (y - 20)..=(y - 7) {
                index_y = 0;
                for n in (i + 19)..=(i + 22) {
                    if n >= pic.width() as i32 {
                        lines[index_y][index_x] = Rgba::<u8>([255, 255, 255, 0]);
                    } else {
                        lines[index_y][index_x] = pic.get_pixel(n as u32, m as u32);
                    }
                    index_y += 1;
                }
                index_x += 1;
            }
            weights[index_of_weigth] += get_weight_bylines(&lines);
            index_x = 0;
            for m in (y + 7)..=(y + 20) {
                index_y = 0;
                for n in (i + 19)..=(i + 22) {
                    if n >= pic.width() as i32 {
                        lines[index_y][index_x] = Rgba::<u8>([255, 255, 255, 0]);
                    } else {
                        lines[index_y][index_x] = pic.get_pixel(n as u32, m as u32);
                    }
                    index_y += 1;
                }
                index_x += 1;
            }
            weights[index_of_weigth] += get_weight_bylines(&lines);
            index_x = 0;
            for m in (y - 20)..=(y - 7) {
                index_y = 0;
                for n in (i - 22)..=(i - 19) {
                    if n < 0 {
                        lines[index_y][index_x] = Rgba::<u8>([255, 255, 255, 0]);
                    } else {
                        lines[index_y][index_x] = pic.get_pixel(n as u32, m as u32);
                    }
                    index_y += 1;
                }
                index_x += 1;
            }
            weights[index_of_weigth] += get_weight_bylines(&lines);
            index_x = 0;
            for m in (y + 7)..=(y + 20) {
                index_y = 0;
                for n in (i - 22)..=(i - 19) {
                    if n < 0 {
                        lines[index_y][index_x] = Rgba::<u8>([255, 255, 255, 0]);
                    } else {
                        lines[index_y][index_x] = pic.get_pixel(n as u32, m as u32);
                    }
                    index_y += 1;
                }
                index_x += 1;
            }
            weights[index_of_weigth] += get_weight_bylines(&lines);
            index_y = 0;
            for m in (y + 19)..=(y + 22) {
                index_x = 0;
                for n in (i - 20)..=(i - 7) {
                    if m >= pic.height() as i32 {
                        lines[index_y][index_x] = Rgba::<u8>([255, 255, 255, 0]);
                    } else {
                        lines[index_y][index_x] = pic.get_pixel(n as u32, m as u32);
                    }
                    index_x += 1;
                }
                index_y += 1;
            }
            weights[index_of_weigth] += get_weight_bylines(&lines);
            index_y = 0;
            for m in (y + 19)..=(y + 22) {
                index_x = 0;
                for n in (i + 7)..=(i + 20) {
                    if m >= pic.height() as i32 {
                        lines[index_y][index_x] = Rgba::<u8>([255, 255, 255, 0]);
                    } else {
                        lines[index_y][index_x] = pic.get_pixel(n as u32, m as u32);
                    }
                    index_x += 1;
                }
                index_y += 1;
            }
            weights[index_of_weigth] += get_weight_bylines(&lines);

            //累计完该像素点对应的权重后权重数组下标递增
            index_of_weigth += 1;
        }
    }
    let mut maxweight: u32 = 0;
    let mut index = 0;
    //获取最大权重对应的下标
    for p in 0..weights.len() {
        if weights[p] > maxweight {
            maxweight = weights[p];
            index = p;
        }
    }

    //将下标转为横坐标
    let x = (index % 280) as f64;
    let x2 = x / width as f64;
    println!("move position (320img): {}px, scale: {}", x, x2);
    
    Ok(x2)
}

type RgbaMy = Rgba<u8>;

//根据四条线(小边)得到对应的权值
fn get_weight_bylines(lines: &Vec<Vec<RgbaMy>>) -> u32 {
    let mut result_weigtht: u32 = 0;
    for lineindex in 0..lines.len() - 1 {
        if lines[lineindex][0][0] == 255 || lines[lineindex + 1][0][0] == 255 {
            continue;
        }
        //四条线两两比对
        let temp = not_similar_between_twolines(&lines[lineindex], &lines[lineindex + 1]);
        //获取最大权值作为该小边对应的权值
        if temp > result_weigtht {
            result_weigtht = temp;
        }
    }
    result_weigtht
}

//得到两个线条的差异值，差异值越大代表是线条的可能性越大，其权重也越大
fn not_similar_between_twolines(line: &Vec<RgbaMy>, line_1: &Vec<RgbaMy>) -> u32 {
    return if is_similar_rgbline(line) && is_similar_rgbline(line_1) {
        //两条线都是同色素线
        if get_distance_bycolor(getaveragecolor(line), getaveragecolor(line_1))//以每条线的平均像素获取两条线的差异值，如果小于某个标准（SIMILAR_RANK可调节）则返回一个设定权重（可调节）
            < SIMILAR_RANK
        {
            SIMILAR_LINE_SIMILAR_WEIGHT
        } else if get_distance_bycolor(getaveragecolor(line), getaveragecolor(line_1))//以每条线的平均像素获取两条线的差异值，如果大于某个标准（NOT_SIMILAR_RANK可调节）则返回一个设定权重（可调节）
            > NOT_SIMILAR_RANK
        {
            SIMILAR_LINE_NOT_SIMILAR_WEIGHT
        } else {
            //如果介于两个标准之间则返回另一个设定权重（可调节）
            SIMILAR_LINE_WEIGHT
        }

        //以下同逻辑
    } else if (!is_similar_rgbline(line)) && (!is_similar_rgbline(line_1)) {
        //两条线都不是同色素线
        if get_distance_bycolor(getaveragecolor(line), getaveragecolor(line_1)) < SIMILAR_RANK {
            NOT_SIMILAR_LINE_SIMILAR_WEIGHT
        } else if get_distance_bycolor(getaveragecolor(line), getaveragecolor(line_1))
            > NOT_SIMILAR_RANK
        {
            NOT_SIMILAR_LINE_NOT_SIMILAR_WEIGHT
        } else {
            NOT_SIMILAR_LINE_WEIGHT
        }
    } else {
        //两条线一条是同色素线一条不是
        if get_distance_bycolor(getaveragecolor(line), getaveragecolor(line_1)) < SIMILAR_RANK {
            LINE_SIMILAR_WEIGHT
        } else if get_distance_bycolor(getaveragecolor(line), getaveragecolor(line_1))
            > NOT_SIMILAR_RANK
        {
            LINE_NOT_SIMILAR_WEIGHT
        } else {
            LINE_WEIGHT
        }
    };
}

//判断是否是同色素线条
fn is_similar_rgbline(line: &Vec<RgbaMy>) -> bool {
    let mut arr_r = [0; 14];
    let mut arr_g = [0; 14];
    let mut arr_b = [0; 14];
    for i in 0..14 {
        arr_r[i] = line[i][0];
        arr_g[i] = line[i][1];
        arr_b[i] = line[i][2];
    }
    if get_variance_fromarr(&arr_r) > SIMILAR_RANK {
        //获取线条的每个像素的R值的方差，大于某个标准（可调节）则可判定不为同色素线条
        return false;
    }
    if get_variance_fromarr(&arr_g) > SIMILAR_RANK {
        //获取G值同上
        return false;
    }
    if get_variance_fromarr(&arr_b) > SIMILAR_RANK {
        //获取B值同上
        return false;
    }
    true
}

//得到线条的平均色素
fn getaveragecolor(line: &Vec<RgbaMy>) -> RgbaMy {
    let mut sumr: u32 = 0;
    let mut sumg: u32 = 0;
    let mut sumb: u32 = 0;
    for color in line {
        sumr += color[0] as u32;
        sumg += color[1] as u32;
        sumb += color[2] as u32;
    }
    Rgba([
        (sumr / line.len() as u32) as u8,
        (sumg / line.len() as u32) as u8,
        (sumb / line.len() as u32) as u8,
        0_u8,
    ])
}

//得到一组数据的方差
fn get_variance_fromarr(arr: &[u8; 14]) -> f64 {
    let mut sum: u32 = 0;
    for val in arr {
        sum += *val as u32;
    }
    let average: f64 = sum as f64 / arr.len() as f64;
    let mut sum: f64 = 0f64;
    for val in arr {
        sum += (*val as f64 - average) * (*val as f64 - average);
    }
    (sum / arr.len() as f64).sqrt()
}

//得到两个像素的色差
fn get_distance_bycolor(color: RgbaMy, color_1: RgbaMy) -> f64 {
    ((color_1[0] as f64 - color[0] as f64) * (color_1[0] as f64 - color[0] as f64)
        + (color_1[1] as f64 - color[1] as f64) * (color_1[1] as f64 - color[1] as f64)
        + (color_1[2] as f64 - color[2] as f64) * (color_1[2] as f64 - color[2] as f64))
        .sqrt()
}
